/*
 * Copyright (C) Francisco Morero Peyrona. All rights reserved.
 *
 * This software is published under the terms of Open Source
 * License version 1.1, a copy of which has been included with this
 * distribution in the License.txt file.
 */
 
package jdbcmanager.diagram.swing;

import java.util.*;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;

/**
 * Shows a scaled map of the entire diagram boards (does not show connectors). 
 *  
 * @author Francisco Morero Peyrona
 */
public final class JDiagramMap extends JPanel
{
   private static final Integer nVISIBLE_AREA_LAYER = JLayeredPane.DEFAULT_LAYER;
   private static final Integer nBOXES_AREA_LAYER   = new Integer( nVISIBLE_AREA_LAYER.intValue() + 1 );
   
   private Hashtable    htBoards   = new Hashtable();
   private JDesktopMap  desktopmap = new JDesktopMap();
   private JVisibleArea pnlVisible = new JVisibleArea();
   
   private double nXRatio = 10;
   private double nYRatio = 10;
   
   //-------------------------------------------------------------------------// 
   
   /**
    * Class constructor
    */
   public JDiagramMap()
   {
      Border raisedbevel  = BorderFactory.createRaisedBevelBorder();
      Border loweredbevel = BorderFactory.createLoweredBevelBorder();
      
      setLayout( new BorderLayout() );
      setBorder( BorderFactory.createCompoundBorder( raisedbevel, loweredbevel ) );
      
      this.desktopmap.add( this.pnlVisible, nVISIBLE_AREA_LAYER );
      
      add( this.desktopmap, BorderLayout.CENTER );
   }

   /**
    * Redefined
    */
   public Dimension getPreferredSize()
   {
      return new Dimension( 120, 120 );
   }
   
   /**
    *  Resizes map
    */
   public void desktopResized( Dimension dimScroll, JViewport viewport )
   {
      Component[] aComp = this.desktopmap.getComponentsInLayer( nBOXES_AREA_LAYER.intValue() );
         
      this.nXRatio = dimScroll.getWidth()  / this.desktopmap.getSize().getWidth();
      this.nYRatio = dimScroll.getHeight() / this.desktopmap.getSize().getHeight();
      
      this.nXRatio = Math.max( this.nXRatio, 5.8 );
      this.nYRatio = Math.max( this.nYRatio, 4.7 );
      
      // Reposition and resize visible area
      viewportChanged( viewport );
      
      // Reposition and resize all boxes representing JBoards
      for( int n = 0; n < aComp.length; n++ )
         ((JBox) aComp[n]).resize();
   }

   /**
    * Reposition and resize visible area and
    * changes map position to be always at bottom-left corner.
    */
   public void viewportChanged( JViewport vp )
   {
      Rectangle vr = vp.getViewRect();      

      // Change the visible area
      this.pnlVisible.setBounds( (int) (vr.getX()      / this.nXRatio), 
                                 (int) (vr.getY()      / this.nYRatio), 
                                 (int) (vr.getWidth()  / this.nXRatio), 
                                 (int) (vr.getHeight() / this.nYRatio) );
                                 
      // Change map position (related to viewport)
      double nX = vr.getX();
      double nY = vr.getY() + vr.getHeight() - getHeight();

      if( getX() != nX || getY() != nY )
         setLocation( (int) nX, (int) nY );
   }

   /**
    * Adds a board to the map.
    * 
    * @param board
    */
   public void boardAdded( JBoard board )
   {
      JBox box = new JBox( board );
           box.setVisible( true );
           box.resize();
      
      this.htBoards.put( new Integer( board.hashCode() ), box );
      this.desktopmap.add( box, nBOXES_AREA_LAYER );
      
   }

   /**
    * Moves corresponding box to passed board to represents the new position.   
    * 
    * @param board The one that was moved.
    */
   public void boardMoved( JBoard board )
   {
      JBox box = (JBox) this.htBoards.get( new Integer( board.hashCode() ) );
           box.move();
   }
   
   /**
    * Removes corresponding box to passed board.
    * 
    * @param board The one that was removed.
    */
   public void boardRemoved( JBoard board )
   {
      JBox box = (JBox) this.htBoards.get( new Integer( board.hashCode() ) );
   
      this.desktopmap.remove( box );
      this.htBoards.remove( box );
   }
  
   /**
    * Resizes corresponding box to passed board.
    * 
    * @param board The one that was resized.
    */
   public void boardResized( JBoard board )
   {
      JBox box = (JBox) this.htBoards.get( new Integer( board.hashCode() ) );
           box.resize();
   }
   
   /**
    * Selects corresponding box to passed board.
    * 
    * @param board The one that was selected.
    */
   public void boardSelected( JBoard board )
   {
      JBox box = (JBox) this.htBoards.get( new Integer( board.hashCode() ) );
           box.setSelected();
   }
      
   //-------------------------------------------------------------------------//
   // INNER CLASS: JDesktopMap --> A JDesktopPane to show mini-boards
   //-------------------------------------------------------------------------//
   private final class JDesktopMap extends JLayeredPane
   {
      private JDesktopMap()
      {
         setOpaque( true );
         setBackground( Color.lightGray );
         setSize( JDiagramMap.this.getPreferredSize() );
      }
   }
   
   //-------------------------------------------------------------------------//
   // INNER CLASS: JVisibleArea --> The rectangle that shows the visible area
   //-------------------------------------------------------------------------//
   private final class JVisibleArea extends JPanel
   {
      private JVisibleArea()
      {
          setBackground( new Color( 0xFF, 0xFF, 0xDD ) );
      }  
   }
   
   //-------------------------------------------------------------------------//
   // INNER CLASS: JBox --> Small boxes representing scaled JBoards
   //-------------------------------------------------------------------------//
   private final class JBox extends JPanel
   {
      private final Color clrUNSELECTED_BACKGROUND = new Color( 0x00, 0x86, 0x99 );      
      private final Color clrSELECTED_BACKGROUND   = new Color( 0x00, 0xAF, 0xFF );
      
      private JBoard parent;
      
      private JBox( JBoard parent )
      {
         this.parent = parent;
         
         // Adds a listener to its parent: the JBoard to change position and size 
         this.parent.addComponentListener( new ComponentAdapter()
         {
            public void componentMoved(   ComponentEvent evt )  { boardMoved(   (JBoard) evt.getComponent() ); }
            public void componentResized( ComponentEvent evt )  { boardResized( (JBoard) evt.getComponent() ); } 
         } );
         
         setBorder( BorderFactory.createLineBorder( Color.black, 1 ) );
         setOpaque( true );
         setSelected();
         resize();
      }
      
      private void move()
      {
         setLocation( (int) (this.parent.getX() / JDiagramMap.this.nXRatio), 
                      (int) (this.parent.getY() / JDiagramMap.this.nYRatio) );
      }
      
      private void resize()
      {
         setBounds( (int) (this.parent.getX()      / JDiagramMap.this.nXRatio), 
                    (int) (this.parent.getY()      / JDiagramMap.this.nYRatio),
                    (int) (this.parent.getWidth()  / JDiagramMap.this.nXRatio),
                    (int) (this.parent.getHeight() / JDiagramMap.this.nYRatio) );
      }
      
      private void setSelected()
      {
         if( this.parent.isSelected() )
         { 
            setBackground( this.clrSELECTED_BACKGROUND );
            desktopmap.moveToFront( this );
         }
         else
         {
            setBackground( this.clrUNSELECTED_BACKGROUND );
         }
      }
   }
}